# !/usr/bin/python3
# -*- coding:utf-8 -*-
"""
@File: seeDemo.py
@Author: lijk34925 
@Date: 2025/4/21 14:59
@desc: 请补充本模块功能说明
"""

import base64
import hashlib
import hmac
import urllib.parse
from datetime import datetime, timedelta, timezone
from uuid import uuid4
from typing import Dict, List

import requests


class SignatureException(Exception):
    def __init__(self, http_status: int, message: str):
        super().__init__(message)
        self.http_status = http_status

    def get_http_code(self) -> int:
        return self.http_status


class SignatureUtil:
    API_VERSION = "v1"
    ISO8601_DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
    ENCODING = "utf-8"
    RESPONSE_FORMAT = "JSON"
    REQUEST_TIMEOUT = 120  # 单位秒

    @classmethod
    def verify(cls, http_method: str, access_key_secret: str, parameters: Dict[str, str]) -> None:
        timestamp_str = parameters.get("TimeStamp")
        if not timestamp_str:
            raise SignatureException(400, "timestamp.empty")

        try:
            timestamp = cls.parse_timestamp(timestamp_str)
        except ValueError:
            raise SignatureException(400, "timestamp.illegal")

        current_time = datetime.now(timezone.utc)
        if (current_time - timestamp).total_seconds() > cls.REQUEST_TIMEOUT:
            raise SignatureException(408, "timestamp.timeout")

        request_signature = parameters.pop("Signature", "")
        signature_method = parameters.get("SignatureMethod", "HMAC-SHA1")
        algorithm = "HmacSHA1" if signature_method == "HMAC-SHA1" else signature_method

        try:
            new_signature = cls.calculate_signature(
                http_method, access_key_secret, algorithm, parameters
            )
        except Exception as e:
            raise SignatureException(400, f"signature.cipherFail: {str(e)}")

        if not hmac.compare_digest(new_signature, request_signature):
            raise SignatureException(400, "signature.disaccord")

    @classmethod
    def parse_timestamp(cls, timestamp_str: str) -> datetime:
        return datetime.strptime(timestamp_str, cls.ISO8601_DATE_FORMAT).replace(tzinfo=timezone.utc)

    @classmethod
    def sign(cls, access_key_id: str, access_key_secret: str, action: str,
             http_method: str, parameters: Dict[str, str]) -> Dict[str, str]:
        if not action:
            raise SignatureException(400, "signature.ActionIsEmpty")

        parameters.update({
            "Action": action,
            "Version": cls.API_VERSION,
            "AccessKeyId": access_key_id,
            "TimeStamp": cls.format_iso8601_date(datetime.now(timezone.utc)),
            "SignatureMethod": "HMAC-SHA1",
            "SignatureVersion": "1.0",
            "SignatureNonce": str(uuid4()),
            "Format": cls.RESPONSE_FORMAT
        })

        signature = cls.calculate_signature(http_method, access_key_secret, "HmacSHA1", parameters)
        parameters["Signature"] = signature
        return parameters

    @classmethod
    def format_iso8601_date(cls, date: datetime) -> str:
        return date.strftime(cls.ISO8601_DATE_FORMAT)

    @classmethod
    def calculate_signature(cls, http_method: str, access_key_secret: str,
                            algorithm: str, parameters: Dict[str, str]) -> str:
        sorted_keys = sorted(parameters.keys())
        canonicalized_query = '&'.join(
            [f"{cls.percent_encode(k)}={cls.percent_encode(parameters[k])}" for k in sorted_keys]
        )

        string_to_sign = f"{http_method}&%2F&{cls.percent_encode(canonicalized_query)}"
        key = f"{access_key_secret}&".encode(cls.ENCODING)

        try:
            digest = hmac.new(
                key,
                string_to_sign.encode(cls.ENCODING),
                hashlib.sha1
            ).digest()
        except Exception as e:
            raise SignatureException(400, f"signature.cipherFail: {str(e)}")

        return base64.b64encode(digest).decode(cls.ENCODING)

    @staticmethod
    def percent_encode(value: str) -> str:
        if value is None:
            return ""

        encoded = urllib.parse.quote_plus(value, encoding='utf-8', errors='strict')
        encoded = encoded.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
        return encoded


def signature_demo(usr, pwd, instance, params):
    # 生成签名
    # params = {}
    signed_params = SignatureUtil.sign(
        access_key_id=usr,
        access_key_secret=pwd,
        action=instance,
        http_method="GET",
        parameters=params
    )
    print(f'signed_params: {signed_params}')
    # 验证签名
    try:
        SignatureUtil.verify(
            http_method="GET",
            access_key_secret=pwd,
            parameters=signed_params
        )
        return signed_params
    except SignatureException as e:
        print(f"Signature Error: {e} (HTTP {e.get_http_code()})")
        return None


if __name__ == '__main__':
    url = 'http://10.20.160.183:8081/acm/api/v1/application'
    usr = 'admin'
    pwd = '539c45a291c555972145cfcf730d7ae2f7a6a9bba2ddcf92ae0ce328ccb1c9473e709c781273582269c89805e7c574018cf3a61a00f2f490261fc732cc3875e8'
    params = {
        'Action': 'EnvironmentList'
    }
    # params_sig = signature_demo(usr, pwd, 'EnvironmentList', params)
    # python
    # {
    #     "Action": "EnvironmentList",
    #     "Version": "v1",
    #     "AccessKeyId": "admin",
    #     "TimeStamp": "2025-04-21T08:18:31Z",
    #     "SignatureMethod": "HMAC-SHA1",
    #     "SignatureVersion": "1.0",
    #     "SignatureNonce": "9b0bd45d-367f-44ca-a38e-4d23a7d63204",
    #     "Format": "JSON",
    #     "Signature": "2++Ea0+PlYOJXScr1GitAlssWG8\u003d"
    # }
    # java
    params_sig = {
    "SignatureVersion": "1.0",
    "Signature": "Tl7r+1M72CqEd564WQrgDWLB7Co=",
    "SignatureMethod": "HMAC-SHA1",
    "Action": "ComponentList",
    "Version": "v1",
    "SignatureNonce": "9009aaff-a496-4ea1-bd86-693e974327d0",
    "TimeStamp": "2025-04-22T06:17:35Z",
    "Format": "JSON",
    "AccessKeyId": "admin"
}
    res = requests.post(url, data=params_sig)
    print(res.json())

    print(res.status_code)

    print(res.text)

    print(res.headers)

    print(res.cookies)



